/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// Note: the example only works with the code within the same release/branch.
package main

import (
	"context"
	"flag"
	"fmt"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/client-go/util/homedir"
	"path/filepath"
	"strings"
	//
	// Uncomment to load all auth plugins
	// _ "k8s.io/client-go/plugin/pkg/client/auth"
	//
	// Or uncomment to load specific auth plugins
	// _ "k8s.io/client-go/plugin/pkg/client/auth/azure"
	// _ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
	// _ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
	// _ "k8s.io/client-go/plugin/pkg/client/auth/openstack"
)

// --kubeconfig=/etc/rancher/k3s/k3s.yaml
func main() {
	var kubeconfig *string
	if home := homedir.HomeDir(); home != "" {
		kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
	} else {
		kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
	}
	flag.Parse()

	// use the current context in kubeconfig
	config, err := clientcmd.BuildConfigFromFlags("", *kubeconfig)
	if err != nil {
		panic(err.Error())
	}

	// create the clientset
	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		panic(err.Error())
	}

	release := "ds-mq-redis-edge"

	ns := "default"
	err = CleanUpDataSyncer1(clientset, ns, release)
	if err != nil {
		panic(err)
	}

	release2 := "ds-redis-td-edge"
	err = CleanUpDataSyncer1(clientset, ns, release2)
	if err != nil {
		panic(err)
	}
}

func GetNodesInformation(clientset *kubernetes.Clientset) {
	pods, err := clientset.CoreV1().Pods("").List(context.TODO(), metav1.ListOptions{})
	if err != nil {
		panic(err.Error())
	}
	fmt.Printf("There are %d pods in the cluster\n", len(pods.Items))

	// Examples for error handling:
	// - Use helper functions like e.g. errors.IsNotFound()
	// - And/or cast to StatusError and use its properties like e.g. ErrStatus.Message
	namespace := "kube-system"
	pod := "coredns-85cb69466-jfxbq"
	_, err = clientset.CoreV1().Pods(namespace).Get(context.TODO(), pod, metav1.GetOptions{})
	if errors.IsNotFound(err) {
		fmt.Printf("Pod %s in namespace %s not found\n", pod, namespace)
	} else if statusError, isStatus := err.(*errors.StatusError); isStatus {
		fmt.Printf("Error getting pod %s in namespace %s: %v\n",
			pod, namespace, statusError.ErrStatus.Message)
	} else if err != nil {
		panic(err.Error())
	} else {
		fmt.Printf("Found pod %s in namespace %s\n", pod, namespace)
	}

	{
		nodes, err := clientset.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{})
		if err != nil {
			fmt.Printf("Found nodes error: %v\n", err)
		} else {
			fmt.Printf("Found nodes number %d in this cluster， kind is: %s\n", len(nodes.Items), nodes.Kind)
			for i, item := range nodes.Items {
				fmt.Printf("Found nodes[%d] %s in cluster %s\n", i, item.Name, item.ClusterName)
				fmt.Printf("labels %s\n", item.Labels) // role

				// add label
				//clientset.CoreV1().Nodes().Update(context.TODO(), item.GetLabels(), metav1.UpdateOptions{})
				item.Labels["hostname"] = item.Labels["kubernetes:io/hostname"]
				fmt.Printf("labels %s\n", item.Labels) // role
				isMaster := item.Labels["node-role.kubernetes.io/master"]
				isControl := item.Labels["node-role.kubernetes.io/control-plane"]
				fmt.Printf("role: isMaster:%s, isControl:%s\n", isMaster, isControl)

				_ = item.Status.Addresses
				fmt.Printf("%v\n", item.Status.Addresses)
				//item.Status.NodeInfo.BootID
				fmt.Printf("node machine id: %v\n", item.Status.NodeInfo.MachineID)
				fmt.Printf("endpoint port: %d\n", item.Status.DaemonEndpoints.KubeletEndpoint.Port)
				fmt.Printf("item.Status.Config: %s\n", item.Status.Config.String())
				//fmt.Printf("%v\n", item.Status.NodeInfo)
				fmt.Printf("NodePhase: %v\n", item.Status.Phase)

				for _, condition := range item.Status.Conditions {
					fmt.Printf("node condition %s is %s\n", condition.Type, condition.Status)
					//if condition.Type == v1.NodeReady {
					//fmt.Printf("it's status's node stats ready is %v\n", condition.Status)
					//}
				}
				fmt.Printf("it's spec is %v\n", item.Status.NodeInfo.KubeletVersion)
				//fmt.Printf("it's spec is %v\n", item.Spec)
			}

			//fmt.Printf("%v", nodes)
		}
	}
}

func CleanUpDataSyncer(client *kubernetes.Clientset, namespace, release string) error {
	clean := true
	fmt.Println("--------------------ConfigMaps-----------------")
	// configmap
	cml, err := client.CoreV1().ConfigMaps(namespace).List(context.TODO(), metav1.ListOptions{
		LabelSelector: fields.OneTermEqualSelector("helmcharts.helm.cattle.io/chart", release).String(),
	})

	if err != nil {
		return err
	} else {
		if len(cml.Items) > 0 {
			for i, item := range cml.Items {
				if strings.HasPrefix(item.Name, release) ||
					strings.HasSuffix(item.Name, release) {
					fmt.Printf("%d %v\n", i, item.Name)
					if clean {
						err := client.CoreV1().ConfigMaps(namespace).Delete(context.TODO(), item.Name, metav1.DeleteOptions{})
						if err != nil {
							fmt.Errorf("delete configmap %s error: %v", item.Name, err)
						}
					}
				}
			}
		}
	}

	fmt.Println("--------------------Deployments-----------------")
	// deploy
	deploys, err := client.AppsV1().Deployments(namespace).List(context.TODO(), metav1.ListOptions{
		LabelSelector: fields.OneTermEqualSelector("helmcharts.helm.cattle.io/chart", release).String(),
		//FieldSelector: fmt.Sprintf("metadata.name=%s-data-sync", release),
		//FieldSelector: fields.OneTermEqualSelector("metadata.name", fmt.Sprintf("data-sync-%s", release)).String(),
	})
	if err != nil {
		return err
	} else {
		if len(deploys.Items) > 0 {
			for i, item := range deploys.Items {
				if strings.HasPrefix(item.Name, release) ||
					strings.HasSuffix(item.Name, release) {
					fmt.Printf("%d %v\n", i, item.Name)
					if clean {
						err := client.AppsV1().Deployments(namespace).Delete(context.TODO(), item.Name, metav1.DeleteOptions{})
						if err != nil {
							fmt.Errorf("delete deployment %s error: %v", item.Name, err)
						}
					}
				}
			}
		}
	}

	fmt.Println("--------------------Pods-----------------")
	// pods
	pods, err := client.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{
		LabelSelector: fmt.Sprintf("helmcharts.helm.cattle.io/chart=%s", release),
		//LabelSelector: fields.OneTermEqualSelector("helmcharts.helm.cattle.io/chart", release).String(),
		//LabelSelector: fmt.Sprintf("app=%s-data-sync", release),
		//LabelSelector: fields.OneTermEqualSelector("app", fmt.Sprintf("data-sync-%s", release)).String(),
	})

	if err != nil {
		return err
	} else {
		if len(pods.Items) > 0 {
			for i, item := range pods.Items {
				if strings.HasPrefix(item.Name, release) {
					fmt.Printf("%d %v\n", i, item.Name)
					if clean {
						err := client.CoreV1().Pods(namespace).Delete(context.TODO(), item.Name, metav1.DeleteOptions{})
						if err != nil {
							fmt.Errorf("delete pod %s error: %v", item.Name, err)
						}
					}
				}
			}
		}
	}
	fmt.Println("--------------------Jobs-----------------")
	// jobs
	jobs, err := client.BatchV1().Jobs(namespace).List(context.TODO(), metav1.ListOptions{
		LabelSelector: fmt.Sprintf("helmcharts.helm.cattle.io/chart=%s", release),
		//LabelSelector: fields.OneTermEqualSelector("helmcharts.helm.cattle.io/chart", release).String(),
	})

	if err != nil {
		return err
	} else {
		if len(jobs.Items) > 0 {
			for i, item := range jobs.Items {
				if strings.HasPrefix(item.Name, release) ||
					strings.HasSuffix(item.Name, release) {
					fmt.Printf("%d %v\n", i, item.Name)
					if clean {
						err := client.BatchV1().Jobs(namespace).Delete(context.TODO(), item.Name, metav1.DeleteOptions{})
						if err != nil {
							fmt.Errorf("delete job %s error: %v", item.Name, err)
						}
					}
				}
			}
		}
	}

	fmt.Println("--------------------HelmPods-----------------")
	// pods
	helmPods, err := client.CoreV1().Pods(namespace).List(context.TODO(), metav1.ListOptions{
		LabelSelector: fmt.Sprintf("helmcharts.helm.cattle.io/chart=%s", release),
		//LabelSelector: fields.OneTermEqualSelector("helmcharts.helm.cattle.io/chart", release).String(),
	})

	if err != nil {
		return err
	} else {
		if len(helmPods.Items) > 0 {
			for i, item := range helmPods.Items {
				if strings.Contains(item.Name, release) {
					fmt.Printf("%d %v\n", i, item.Name)
					err := client.CoreV1().Pods(namespace).Delete(context.TODO(), item.Name, metav1.DeleteOptions{})
					if err != nil {
						fmt.Errorf("delete pod %s error: %v", item.Name, err)
					}
				}
			}
		}
	}

	return nil
}

// release删除后，清理资源
func CleanUpDataSyncer1(client *kubernetes.Clientset, namespace, release string) error {
	//dryRun := []string{"All"}
	dryRun := []string{}
	helmLabel := "helmcharts.helm.cattle.io/chart"
	// delete config map
	err := deleteConfigMap(client, namespace, metav1.ListOptions{
		LabelSelector: fmt.Sprintf("%s=%s", helmLabel, release),
	}, dryRun)
	if err != nil {
		return err
	}
	err = deleteConfigMap(client, namespace, metav1.ListOptions{
		FieldSelector: fmt.Sprintf("metadata.name=chart-content-%s", release),
	}, dryRun)
	if err != nil {
		return err
	}
	err = deleteConfigMap(client, namespace, metav1.ListOptions{
		FieldSelector: fmt.Sprintf("metadata.name=chart-values-%s", release),
	}, dryRun)
	if err != nil {
		return err
	}

	// delete deployment
	err = deleteDeployment(client, namespace, metav1.ListOptions{
		LabelSelector: fmt.Sprintf("%s=%s", helmLabel, release),
	}, dryRun)
	if err != nil {
		return err
	}

	// delete job
	err = deleteJob(client, namespace, metav1.ListOptions{
		LabelSelector: fmt.Sprintf("%s=%s", helmLabel, release),
	}, dryRun)
	if err != nil {
		return err
	}

	// delete pod
	err = deletePod(client, namespace, metav1.ListOptions{
		LabelSelector: fmt.Sprintf("%s=%s", helmLabel, release),
	}, dryRun)
	if err != nil {
		return err
	}

	return nil
}

func deleteConfigMap(client *kubernetes.Clientset, namespace string, listOptions metav1.ListOptions, dryRun []string) error {
	cmi := client.CoreV1().ConfigMaps(namespace)
	// configmap
	cm1, err := cmi.List(context.TODO(), listOptions)

	if err != nil {
		return err
	} else {
		if len(cm1.Items) > 0 {
			for i, item := range cm1.Items {
				fmt.Printf("%d %v\n", i, item.Name)
				err := cmi.Delete(context.TODO(), item.Name, metav1.DeleteOptions{
					DryRun: dryRun,
				})
				if err != nil {
					fmt.Errorf("delete configmap %s error: %v", item.Name, err)
				}
			}
		}
	}

	return nil
}

func deleteDeployment(client *kubernetes.Clientset, namespace string, listOptions metav1.ListOptions, dryRun []string) error {
	cmi := client.AppsV1().Deployments(namespace)
	// deployment
	deploys, err := cmi.List(context.TODO(), listOptions)

	if err != nil {
		return err
	} else {
		if len(deploys.Items) > 0 {
			for i, item := range deploys.Items {
				fmt.Printf("%d %v\n", i, item.Name)
				err := cmi.Delete(context.TODO(), item.Name, metav1.DeleteOptions{
					DryRun: dryRun,
				})
				if err != nil {
					fmt.Errorf("delete deployment %s error: %v", item.Name, err)
				}
			}
		}
	}

	return nil
}

func deleteJob(client *kubernetes.Clientset, namespace string, listOptions metav1.ListOptions, dryRun []string) error {
	cmi := client.BatchV1().Jobs(namespace)
	// configmap
	jobs, err := cmi.List(context.TODO(), listOptions)

	if err != nil {
		return err
	} else {
		if len(jobs.Items) > 0 {
			for i, item := range jobs.Items {
				fmt.Printf("%d %v\n", i, item.Name)
				err := cmi.Delete(context.TODO(), item.Name, metav1.DeleteOptions{
					DryRun: dryRun,
				})
				if err != nil {
					fmt.Errorf("delete job %s error: %v", item.Name, err)
				}
			}
		}
	}

	return nil
}

func deletePod(client *kubernetes.Clientset, namespace string, listOptions metav1.ListOptions, dryRun []string) error {
	cmi := client.CoreV1().Pods(namespace)
	// configmap
	pods, err := cmi.List(context.TODO(), listOptions)

	if err != nil {
		return err
	} else {
		if len(pods.Items) > 0 {
			for i, item := range pods.Items {
				fmt.Printf("%d %v\n", i, item.Name)
				err := cmi.Delete(context.TODO(), item.Name, metav1.DeleteOptions{
					DryRun: dryRun,
				})
				if err != nil {
					fmt.Errorf("delete pod %s error: %v", item.Name, err)
				}
			}
		}
	}

	return nil
}
